/**
 * 用途         :囊括了一些常用的数学方法
 * 
 * 
 * 感谢白玉无冰大佬的四元数教程
 */
export default class MathUtil {
    /**
     * 获取在num1 num2之间的数整数  左闭右开[num1,num2)
     * @param num1 
     * @param num2 
     */
    public static getRangeRandom(num1,num2){
        if (num2 <= num1){
            Log('传入参数出错!')
        }
        return Math.floor(Math.random()*(num2-num1))+num1;
    }
    /**
     * 获取两点之间的距离，如果是向量请直接相减再使用mag
     * @param pointA 
     * @param pointB 
     */
    public static Pdistance(pointA, pointB) {
        var dx = Math.abs(pointA.x - pointB.x);
        var dy = Math.abs(pointA.y - pointB.y);
        var dis = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
        return dis;
    }
    /**
     * Sin 角度
     */
    public static Sin(angle) {
        return Math.sin(angle * (Math.PI / 180));
    }
    /**
     * Cos 角度
     */
    public static Cos(angle) {
        return Math.cos(angle * (Math.PI / 180));
    }
    /**
     * 角度转弧度
     * @param angle 
     */
    public static Radian(angle) {
        return angle * (Math.PI / 180);
    }
    /**
     * 欧拉角转四元数
     * @param vec 
     */
    public static fromEuler(vec:IVec3Like){
        let quat = new cc.Quat(0,0,0,0);
        cc.Quat.fromEuler(quat,vec.x,vec.y,vec.z);
        return quat;
    }
    /**
    * @zh 根据旋转轴和旋转弧度计算四元数
    */
    public static fromAxisAngle(axis: cc.Vec3, rad: number): cc.Quat {
        rad = rad * 0.5; // 为什么要除以2？因为公式推导出来的！
        const s = Math.sin(rad);
        let out = new cc.Quat();
        out.x = s * axis.x;
        out.y = s * axis.y;
        out.z = s * axis.z;
        out.w = Math.cos(rad);
        return out;
    }
    /**
     * 用手划拉一个物体以它中心旋转，用于360观看某物体
     * @param touch  触摸事件的touch 要是3d的 
     * @param node 
     */
    public static touchNodeRotate(touch,node){
        const delta = touch.getDelta();
        // 自传
        // 这个物体模型‘锚点’在正中心效果比较好
        // 垂直的轴，右手  
        //  
        //  旋转轴
        //  ↑
        //  ---> 触摸方向
        const axis = cc.v3(-delta.y, delta.x, 0); //旋转轴，根据相似三角形求出
        const rad = delta.length() * 1e-2; //旋转角度
        const quat_cur = node.getRotation(); //当前的四元数
        let temp = new cc.Quat()
        cc.Quat.rotateAround(temp, quat_cur, axis.normalize(), rad); //当面的四元数绕旋转轴旋转
        // 旋转后的结果 / 当前的四元数 / 旋转轴 / 旋转四元数
        node.setRotation(temp);
    }
    /**
     * 
     * @param node_axi 旋转点（绕轴的起点）的世界坐标
     * @param node_touch_axi_role 当前点
     * @param angel 角度
     * @param axis2 旋转的轴
     */
    public static rotateByAxis(point,node_touch_axi_role,angel,axis2 = cc.Vec3.UP) {
        // 绕轴转
        // 这里选取轴朝上
        // const axis2 = Vec3.UP;//旋转轴
        const rad2 = angel; //旋转角度
        // 计算坐标
        // const point = node_axi.worldPosition; //旋转点
        const point_now = node_touch_axi_role.worldPosition; // 当前点的位置
        // 算出坐标点的旋转四元数
        let tempQuat = new cc.Quat();
        let tempV3 = new cc.Vec3();
        cc.Quat.fromAxisAngle(tempQuat, axis2, rad2);
        // 计算旋转点和现有点的向量
        cc.Vec3.subtract(tempV3, point_now, point);
        // 计算旋转后的向量
        cc.Vec3.transformQuat(tempV3, tempV3, tempQuat)
        // 计算旋转后的点
        cc.Vec3.add(tempV3, point, tempV3);
        node_touch_axi_role.setWorldPosition(tempV3);
        
        // 计算朝向
        // 这么旋转会按原始的朝向一起旋转
        const quat_now = node_touch_axi_role.worldRotation;
        cc.Quat.rotateAround(tempQuat, quat_now, axis2, rad2);
        cc.Quat.normalize(tempQuat, tempQuat);
        node_touch_axi_role.setWorldRotation(tempQuat);
    }
    /**
     * 检查传参是否是数字，如果不是返回0
     * @param localNum 
     */
    public static CheckNumNaN(localNum) {
        let result = 0;
        if (localNum == 'NaN') {
            result = 0;
        }
        else {
            result = parseInt(localNum);
            if (isNaN(result)) {
                result = 0;
            }
        }
        return result;
    }
    /**
     * 将V3向量的z去掉，返回v2
     * @param ve3 
     */
    public static vec3to2(ve3: cc.Vec3) {
        let vec2 = new cc.Vec2(ve3.x, ve3.y);
        return vec2;
    }
    /**
     * 基于字符串数值的a+b
     * @param {string} a
     * @param {string} b
     */
    public static plusby(a, b) {
        a = MathUtil.numerify(a)
        b = MathUtil.numerify(b)

        if (a === '0') {
            return b
        }
        else if (b === '0') {
            return a
        }

        var [ia, da = '0'] = a.split('.')
        var [ib, db = '0'] = b.split('.')

        // 是否为负数
        var na = false
        var nb = false
        if (ia.indexOf('-') === 0) {
            ia = ia.substring(1)
            na = true
        }
        if (ib.indexOf('-') === 0) {
            ib = ib.substring(1)
            nb = true
        }

        // 一正一负相当于相减
        if (na && !nb) {
            return MathUtil.minusby(b, a.substring(1))
        }
        if (nb && !na) {
            return MathUtil.minusby(a, b.substring(1))
        }

        // 创建一个整数相加函数
        const plus = (x, y) => {
            let xr = x.split('').reverse()
            let yr = y.split('').reverse()
            let len = Math.max(xr.length, yr.length)
            let items = []
            for (let i = 0; i < len; i++) {
                let xv = xr[i] || '0'
                let yv = yr[i] || '0'
                items[i] = ((+xv) + (+yv)) + ''
            }

            let sum = items.reduce((sum, item, index) => {
                let sumlen = sum.length
                // 如果之前相加进了一位
                if (sumlen > index) {
                    let borrow = sum.substring(0, 1)
                    let placed = sum.substring(1)
                    let next = (+borrow + +item) + ''
                    return next + placed
                }
                else {
                    return item + sum
                }
            }, '')
            return sum
        }

        // 补齐位数用以相加
        const dalen = da.length
        const dblen = db.length
        const dlen = Math.max(dalen, dblen)
        if (dalen < dlen) {
            da = MathUtil.padRight(da, dlen, '0')
        }
        if (dblen < dlen) {
            db = MathUtil.padRight(db, dlen, '0')
        }

        const ta = ia + da
        const tb = ib + db

        var sum = plus(ta, tb)

        // 还原小数位数
        var sumr = sum.split('')
        var sumlen = sumr.length
        var index = sumlen - dlen
        sumr.splice(index, 0, '.')
        sum = sumr.join('')

        sum = MathUtil.clearNumberZero(sum)
        sum = sum === '' ? '0' : sum

        // 都是负数
        if (sum !== '0' && na && nb) {
            sum = '-' + sum
        }

        return sum
    }
    /**
     * 基于字符串形式数值的a-b
     * @param {string} a
     * @param {string} b
     */
    public static minusby(a, b) {
        a = MathUtil.numerify(a)
        b = MathUtil.numerify(b)

        if (b === '0') {
            return a
        }
        else if (a === '0') {
            if (b.indexOf('-') === 0) {
                return b.substring(1)
            }
            else {
                return '-' + b
            }
        }
        else if (a === b) {
            return '0'
        }

        var [ia, da = '0'] = a.split('.')
        var [ib, db = '0'] = b.split('.')

        // 是否为负数
        var na = false
        var nb = false
        if (ia.indexOf('-') === 0) {
            ia = ia.substring(1)
            na = true
        }
        if (ib.indexOf('-') === 0) {
            ib = ib.substring(1)
            nb = true
        }

        // 一正一负相当于相加
        if (na && !nb) {
            return MathUtil.plusby(a, '-' + b)
        }
        if (nb && !na) {
            return MathUtil.plusby(a, b.substring(1))
        }

        // 当b大于a的时候，先反过来计算，然后再取反
        if (MathUtil.compare(b, a) > 0) {
            let diff = MathUtil.minusby(b, a)
            return '-' + diff
        }

        const minus = (x, y) => {
            let xr = x.split('').reverse()
            let yr = y.split('').reverse()
            let len = Math.max(xr.length, yr.length)
            let items = []
            for (let i = 0; i < len; i++) {
                let xv = xr[i] || '0'
                let yv = yr[i] || '0'
                items[i] = {
                    xv,
                    yv,
                }
            }

            let isBorrowed = false
            let diff = items.reduce((diff, item, index) => {
                let { xv, yv } = item

                xv = +xv
                yv = +yv

                // 如果被借位，则一开始自己就要减1
                if (isBorrowed) {
                    xv--
                }

                // 向前借位
                if (xv < yv) {
                    isBorrowed = true
                    xv += 10
                }
                else {
                    isBorrowed = false
                }

                let v = xv - yv
                diff = v + diff

                return diff
            }, '')

            return diff
        }

        // 补齐位数用以相减
        const dalen = da.length
        const dblen = db.length
        const dlen = Math.max(dalen, dblen)
        if (dalen < dlen) {
            da = MathUtil.padRight(da, dlen, '0')
        }
        if (dblen < dlen) {
            db = MathUtil.padRight(db, dlen, '0')
        }

        const ta = ia + da
        const tb = ib + db

        var diff = minus(ta, tb)

        // 还原小数位数
        var diffr = diff.split('')
        var difflen = diffr.length
        var index = difflen - dlen
        diffr.splice(index, 0, '.')
        diff = diffr.join('')

        diff = MathUtil.clearNumberZero(diff)
        diff = diff === '' ? '0' : diff

        return diff
    }

    /**
     * 字符串形式数值a*b
     * @param {string} a
     * @param {string} b
     */
    public static multiplyby(a, b) {
        a = MathUtil.numerify(a)
        b = MathUtil.numerify(b)

        // 0值快速返回
        if (a === '0' || b === '0') {
            return '0'
        }
        else if (a === '1') {
            return b
        }
        else if (b === '1') {
            return a
        }
        else if (a === '-1') {
            if (b.indexOf('-') === 0) {
                return b.substring(1)
            }
            else {
                return '-' + b
            }
        }
        else if (b === '-1') {
            if (a.indexOf('-') === 0) {
                return a.substring(1)
            }
            else {
                return '-' + a
            }
        }
        // 特殊处理那种整除100000的
        else if (/^10+/.test(b)) {
            let wei = Math.log10(b)
            let value = MathUtil.numerify(a)
            let [integerPart, decimalPart = ''] = value.split('.')
            let decimalLen = decimalPart.length
            // 移动小数点位置
            if (decimalLen <= wei) {
                value = integerPart + MathUtil.padRight(decimalPart, wei, '0')
            }
            else {
                value = integerPart + decimalPart.substring(0, wei) + '.' + decimalPart.substring(wei)
            }
            value = MathUtil.clearNumberZero(value)
            return value
        }

        /**
         * 大数乘法
         * 思路：逐位相乘，不算进位；最后算进位并拼接字符串
         * @param {number} a 被乘数
         * @param {number} b 乘数
         */
        const multiply = (a, b) => {
            const result = []
            const aArr = a.toString().split('').map(t => parseInt(t))
            const bArr = b.toString().split('').map(t => parseInt(t))
            const aLen = aArr.length
            const bLen = bArr.length

            // 逐位相乘，不算进位，与计算方向无关
            for (let bIndex = bLen - 1; bIndex >= 0; bIndex--) {
                for (let aIndex = aLen - 1; aIndex >= 0; aIndex--) {
                    let index = bIndex + aIndex
                    if (!result[index]) {
                        result[index] = 0
                    }
                    result[index] += bArr[bIndex] * aArr[aIndex]
                }
            }

            // 因为是从左到右的计算顺序，所以进位要反向
            // （也方便最高位进位时，数组可扩）。
            result.reverse()
            // 最高位可能会进位，所以每次循环重新计算length。
            for (let i = 0; i < result.length; i++) {
                if (!result[i]) {
                    result[i] = 0
                }

                let more = parseInt(String(result[i] / 10))
                if (more > 0) {
                    if (!result[i + 1]) {
                        result[i + 1] = 0
                    }
                    result[i + 1] += more
                }
                result[i] = result[i] % 10
            }
            result.reverse()

            return result.join('')
        }

        var [ia, da = '0'] = a.split('.')
        var [ib, db = '0'] = b.split('.')

        // 是否为负数
        var na = false
        var nb = false
        if (ia.indexOf('-') === 0) {
            ia = ia.substring(1)
            na = true
        }
        if (ib.indexOf('-') === 0) {
            ib = ib.substring(1)
            nb = true
        }

        // 负负为正
        var isNegative = false
        if ((na && !nb) || (!na && nb)) {
            isNegative = true
        }

        // var iProd = multiply(ia, ib)
        // var dProd = multiply(da, db)

        // dProd = MathUtil.padLeft(dProd, da.length + db.length, '0') // 补全小数位数

        // var value = iProd + '.' + dProd
        var pointLength = da.length + db.length;
        var value = multiply(ia+da,ib+db);
        value = MathUtil.padLeft(value,pointLength,'0');
        if (value.length == pointLength){
            value = '0.'+value;
        }
        else if (value.length < pointLength){
            value = '0';
            Log('这个有点不对劲 需要pass')
        }
        else{
            let index = value.length - pointLength;
            value = value.substring(0,index) + '.' + value.substr(index);
        }

        value = MathUtil.clearNumberZero(value)
        value = (isNegative ? '-' : '') + value
        value = value === '' ? '0' : value

        return value
    }
    /**
     * 字符串形式数值a/b
     * @param {*} a
     * @param {*} b
     * @param {*} decimal 小数位数，当除不尽时，保留多少位小数，默认15位
     */
    public static InfiniteDecimalLength = 15; // 无限小数保留位数

    public static divideby(a, b, decimal = MathUtil.InfiniteDecimalLength) {

        a = MathUtil.numerify(a)
        b = MathUtil.numerify(b)

        // 除数不能为0
        if (b === '0') {
            throw new Error('除数不能为0')
        }

        if (a === '0') {
            return '0'
        }
        else if (b === '1') {
            return a
        }
        else if (a === b) {
            return '1'
        }
        // 特殊处理那种整除100000的
        else if (/^10+/.test(b)) {
            let wei = Math.log10(b)
            let value = MathUtil.numerify(a)
            let [integerPart, decimalPart = ''] = value.split('.')
            let integerLen = integerPart.length
            // 移动小数点位置
            if (integerLen <= wei) {
                value = '0.' + MathUtil.padLeft(integerPart, wei, '0') + decimalPart
            }
            else {
                let pos = integerLen - wei
                let left = integerPart.substring(0, pos)
                let right = integerPart.substring(pos)
                value = left + '.' + right + decimalPart
            }
            value = MathUtil.clearNumberZero(value)
            return value
        }

        var [ib, db = ''] = b.split('.')

        // 除数被除数都同时扩大，使除数成为整数
        if (db.length) {
            let len = db.length
            let pow = Math.pow(10, len)
            a = MathUtil.multiplyby(a, pow)
            b = MathUtil.multiplyby(b, pow)
        }

        var [ia, da = ''] = a.split('.')

        // 是否为负数
        var na = false
        var nb = false
        if (ia.indexOf('-') === 0) {
            ia = ia.substring(1)
            na = true
        }
        if (b.indexOf('-') === 0) {
            b = b.substring(1)
            nb = true
        }

        // 两个正整数相除
        const divide = (x, y) => {
            const uselen = y.length
            const result = []

            // 取出一组
            var waitforcompare = x.substr(0, uselen)
            var waittouse = x.substring(uselen)

            var stillhave = waitforcompare // 减去y之后剩下的值
            var inrange = 0

            do {
                let c
                while (c = MathUtil.compare(stillhave, y) >= 0) {
                    if (c > 0) {
                        inrange++
                        stillhave = MathUtil.minusby(stillhave, y) // 减去1次
                    }
                    else if (c === 0) {
                        inrange++
                        stillhave = ''
                        break
                    }
                }

                let stillhavelen = stillhave.length
                // 从x的头部取出需要的部分组成新的waitforcompare
                let nextlen = uselen - stillhavelen
                nextlen = nextlen > 0 ? nextlen : 1
                waitforcompare = (stillhave === '0' ? '' : stillhave) + waittouse.substr(0, nextlen)
                waittouse = waittouse.substring(nextlen)

                result.push(inrange)
                stillhave = waitforcompare // 当stillhave=''时，会跳出循环
                inrange = 0

                // 末尾为 0，且整除的情况下，循环会被破坏，这种情况要特殊处理，直接将剩余的 00 添加到商的末尾即可
                if (waittouse === '' && /^0+$/.test(waitforcompare)) {
                    result.push(waitforcompare)
                    stillhave = ''
                    break
                }
            } while (MathUtil.compare(stillhave, y) >= 0)

            var remainder = stillhave || '0'
            var quotient = result.join('')

            remainder = MathUtil.clearNumberZero(remainder)
            quotient = MathUtil.clearNumberZero(quotient)

            return { remainder, quotient }
        }

        var dvi = divide(ia, b) // 整数部分得到的结果
        var { remainder, quotient } = dvi
        var value = quotient

        if (da) {
            remainder = remainder === '0' ? da : remainder + da // 连接小数部分，准备计算小数部分的商
        }
        else {
            remainder = remainder + '0'
        }

        if (remainder && remainder !== '0') {
            var result = ''
            var nextto = remainder
            // Log(value, nextto, b, /[1-9]/.test(nextto), divide(nextto, b))
            while (/[1-9]/.test(nextto)) {
                let dvd = divide(nextto, b)
                let { remainder, quotient } = dvd
                result += quotient

                if (remainder === '0') {
                    break
                }

                nextto = remainder + '0'

                // 当小数位数超出规定的位数时，跳出
                if (result.length > decimal) {
                    break
                }
            }
            value = quotient + '.' + result
        }

        value = MathUtil.clearNumberZero(value)

        if ((na && !nb) || (!na && nb)) {
            value = '-' + value
        }

        return value
    }
    /**
     * 幂运算
     * @param a   值 
     * @param num  次方
     */
    public static powerStr(a,num){
        let count = 0;
        a = MathUtil.numerify(a);
        let sum = '1';
        while(count < num){
            sum = MathUtil.multiplyby(sum,a);
            count +=1;
        }
        return sum;
    }
    /**
     * 字符串算式计算，仅支持加减乘除
     * @param {*} exp
     * @example
     * calculate('128+12*24-(132-87)')
     */
    public static calculate(exp, decimal = MathUtil.InfiniteDecimalLength) {
        const contains = (str, items) => {
            for (let i = 0, len = items.length; i < len; i++) {
                let item = items[i]
                if (str.indexOf(item) > -1) {
                    return true
                }
            }
            return false
        }

        if (!/^[\(\-]?[0-9]+[0-9\+\-\*\/\(\)]*[0-9\)]$/.test(exp)) {
            throw new Error('算式中包含不允许的内容')
        }
        if (contains(exp, ['---', '++', '**', '//'])) { // -- 是被允许了，例如 -2--3 这样的算式是合法的，实际上，也只有 - 可以被跟在另外一个符合后面
            throw new Error('算式中包含重复的运算符')
        }
        if (contains(exp, ['-*', '-/', '+*', '+/'])) {
            throw new Error('酸式中包含非法的连续运算符')
        }
        if (exp.indexOf(')(') > -1) {
            throw new Error('算式中不允许包含连续括号')
        }
        if (exp.indexOf('()') > -1) {
            throw new Error('算式中不允许包含空括号')
        }
        if (/\)[0-9]/.test(exp)) {
            throw new Error('算式中，括号后面不能直接跟数字')
        }
        if (/[0-9]\(/.test(exp)) {
            throw new Error('算式中，括号不能直接跟在数字后面')
        }

        const parse = (exp) => {
            let inGroup = 0 // 数字代表括号层级，括号里面还能套括号
            let exparr = []
            let expstr = ''
            let groups = []
            let groupstr = ''
            for (let i = 0, len = exp.length; i < len; i++) {
                let char = exp.charAt(i)
                if (char === '(') {
                    // 子括号，在外面的括号内部，只需要将字符串原始连接给该组即可，放到后面处理
                    if (inGroup) {
                        groupstr += char
                    }
                    else {
                        if (expstr) {
                            exparr.push(expstr)
                            expstr = ''
                        }
                    }
                    inGroup++
                }
                else if (char === ')') {
                    if (!inGroup) {
                        throw new Error('算式不正确，非法反括号，位置：' + groupstr + ')')
                    }

                    // 顶层括号结束
                    if (inGroup === 1) {
                        if (groupstr) {
                            let index = groups.length
                            exparr.push(index)
                            groups.push(groupstr)
                            groupstr = ''
                        }
                    }
                    else {
                        groupstr += char
                    }
                    inGroup--
                }
                else if (inGroup) {
                    groupstr += char
                }
                else {
                    if (/[\+\-\*\/]/.test(char)) {
                        if (expstr) {
                            exparr.push(expstr)
                        }
                        expstr = ''
                        exparr.push(char)
                    }
                    else {
                        expstr += char
                    }
                }
            }

            if (inGroup) {
                throw new Error('括号没有正确关闭，请检查算式')
            }

            // 最后一个数
            if (expstr) {
                exparr.push(expstr)
            }

            // 对于运算中的负数，直接将它组合到后面一位的数字中去
            const exparr2 = []
            for (let i = 0, len = exparr.length; i < len; i++) {
                const current = exparr[i]
                const prev = exparr[i - 1]
                const next = exparr[i + 1]
                // 遇到减号
                if (current === '-') {
                    // 负号前面是符号，表示当前数字可能是负数
                    if (i === 0 || MathUtil.inArray(prev, ['*', '/', '+', '-'])) {
                        // 在一个负值前面添加负号，表示正数
                        if (next === '-') {
                            i++
                            continue
                        }
                        // 正数前面肯定就是负数了
                        else if (next === '+') {
                            let nextnext = exparr[i + 2]
                            let text = '-' + nextnext
                            exparr2.push(text)
                            i += 2
                        }
                        // 后面是数
                        else {
                            let text = '-' + next
                            exparr2.push(text)
                            i++
                        }
                    }
                    else {
                        exparr2.push(current)
                    }
                }
                else {
                    exparr2.push(current)
                }
            }

            // 解析前面创建的括号组，并将该处用一个数组替换
            const expsrc = []
            exparr2.forEach((item, i) => {
                if (MathUtil.isNumber(item)) {
                    item = groups[item]
                    item = parse(item)
                }
                expsrc.push(item)
            })

            var expast = []

            // 将乘除法运算进行提升
            if (contains(exp, ['+', '-']) && contains(exp, ['*', '/'])) {
                let combine = []
                let started = false
                for (let i = 0; i < expsrc.length; i++) {
                    let current = expsrc[i]
                    // 乘除法开始
                    if (!started && (current === '*' || current === '/')) {
                        let prev = expast.pop()
                        combine.push(prev)
                        combine.push(current)
                        started = true
                    }
                    // 乘除法结束
                    // 减号位置需要注意，因为减号可能存在于乘除法中间
                    else if (started) {
                        if (current === '+' || (!MathUtil.inArray(combine[combine.length - 1], ['*', '/']) && current === '-')) {
                            expast.push(combine)
                            expast.push(current)
                            started = false
                            combine = []
                        }
                        else if (i === (expsrc.length - 1)) {
                            combine.push(current)
                            expast.push(combine)
                            started = false
                            combine = []
                        }
                        else {
                            combine.push(current)
                        }
                    }
                    else {
                        expast.push(current)
                    }
                }
            }
            // 在没有混合运算的情况下，直接返回即可
            else {
                expast = expsrc
            }

            // expres是一个多维数组，到底多少层未知，主要看括号分组情况
            // 这个数组的最顶层是整个算式的解析结果，模式如下：
            // [ '1', '+', '2' ]
            // [ '1', '+', [ '2', '*', '3' ], '+', [ '4', '/', '2' ] ]
            // 内部的数组代表一个小括号，会在运算时先执行
            return expast
        }

        // 运算元，从左往右依次进行计算
        const execute = (expast) => {
            // 先计算出优先级高的部分
            const exparr = []
            expast.forEach((item) => {
                if (Array.isArray(item)) {
                    item = execute(item)
                }
                exparr.push(item)
            })

            // 由于 expast 中进行了分组，因此，同一个级别的运算只会以同一级别运算符为集合，因此，不需要考虑加减和乘除同时混合的情况，只需要对乘除进行处理即可
            // 调整顺序，如果遇到乘除法连续的情况，先乘法，再除法，这样可以减少除不尽带来的误差，例如 10/3*3 调整为 10*3/3 则不会出现除不尽
            var expres = []
            const leftres = []
            const rightres = []
            for (let i = 0, len = exparr.length; i < len; i++) {
                const current = exparr[i]
                const next = exparr[i + 1]
                if (current === '*') {
                    leftres.push(current)
                    leftres.push(next)
                    i++
                }
                else if (current === '/') {
                    rightres.push(current)
                    rightres.push(next)
                    i++
                }
                else {
                    expres.push(current)
                }
            }
            expres = expres.concat(leftres).concat(rightres)

            // 开始执行连续计算逻辑
            let result = ''
            for (let i = 0, len = expres.length; i < len; i++) {
                let current = expres[i]
                if (i === 0) {
                    result = current === '-' ? '0' : current
                }
                if (/[\+\-\*\/]/.test(current)) {
                    let next = expres[i + 1]
                    if (current === '+') {
                        result = MathUtil.plusby(result, next)
                    }
                    else if (current === '-') {
                        result = MathUtil.minusby(result, next)
                    }
                    else if (current === '*') {
                        result = MathUtil.multiplyby(result, next)
                    }
                    else if (current === '/') {
                        result = MathUtil.divideby(result, next, decimal)
                    }
                }
            }

            return result
        }

        const expast = parse(exp)
        const result = execute(expast)
        return result
    }
    /**
     * 比较两个数的大小，返回1表示a>b，返回-1表示a<b，返回0表示a=b
     * @param {*} a
     * @param {*} b
     */
    public static compare(a, b) {
        a = MathUtil.numerify(a)
        b = MathUtil.numerify(b)

        var [ia, da = ''] = a.split('.')
        var [ib, db = ''] = b.split('.')

        // n, m 都是正整数
        const compare2 = (n, m) => {
            if (n.length > m.length) {
                return 1
            }
            else if (n.length < m.length) {
                return -1
            }
            else {
                for (let i = 0, len = n.length; i < len; i++) {
                    let nv = n.charAt(i)
                    let mv = m.charAt(i)
                    if (+nv > +mv) {
                        return 1
                    }
                    else if (+nv < +mv) {
                        return -1
                    }
                }
                return 0
            }
        }

        // x, y都是整数，但可正可负
        const compare = (x, y) => {
            let nx = x.indexOf('-') === 0
            let ny = y.indexOf('-') === 0

            // x > 0 > y
            if (!nx && ny) {
                return 1
            }
            // y > 0 > x
            else if (nx && !ny) {
                return -1
            }
            // x, y < 0
            else if (nx && ny) {
                x = x.substring(1)
                y = y.substring(1)
                let result = compare2(x, y)
                return -result
            }
            // x, y > 0
            else if (!nx && !ny) {
                return compare2(x, y)
            }
        }

        const ci = compare(ia, ib)
        if (ci) {
            return ci
        }

        // 小数部分长度补全相同长度来比较
        const dalen = da.length
        const dblen = db.length
        const dlen = Math.max(dalen, dblen)
        if (dalen < dlen) {
            da = MathUtil.padRight(da, dlen, '0')
        }
        if (dblen < dlen) {
            db = MathUtil.padRight(db, dlen, '0')
        }
        const cd = compare(da, db)
        if (cd) {
            return cd
        }

        return 0
    }
    /**
     * 数字字符串取整
     * @param str 
     */
    public static floorStr(str){
        if (!MathUtil.isNumeric(str)){
            Log('该字符串不是数字,请注意:',str)
            return str;
        }
        let point = str.indexOf('.');
        if(point == 0){
            str = '0';
        }
        else if (point > 0){
            str = str.substring(0,point);
        }
        return str;
    }
    /**
     * 转换单位(会去除小数)
     * @param num 转换的数字或者字符串数字
     */
    public static changeCoinUnit(num) {
        /**
         * K: 3
         * M: 6
         * G: 9
         * T: 12
         * P: 15
         * E: 18
         * Z: 21
         * A: 24
         * B: 27
         * C: 30
         * D: 33
         * aa : 36
         * bb : 39
         * cc : 42
         */
        var str = '' + num;
        str = MathUtil.floorStr(str);
        var length = str.length;
        var i = 0;
        var unitArray = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z','A','B','C','D','aa','bb','cc'];
        if (length < 5) //不上单位          1000
        {
            return str;
        }
        else {
            while (length > 3) {
                i += 1;
                length -= 3;
            }
            if (length == 3) {
                var subs = str.substring(0, 3);
            }
            else if (length == 2) {
                var subs = str.substring(0, 2) + '.' + str.substring(2, 3);
            }
            else if (length == 1) {
                var subs = str.substring(0, 1) + '.' + str.substring(1, 2);
            }
            if (i < unitArray.length) {
                var result = subs + unitArray[i];
            }
            else {
                var result = 'Full';
            }
            return result;
        }

    }
    /**
     * 对字符串向左补位
     * @param {*} str 原始字符串
     * @param {*} len 要求字符串应该为多长
     * @param {*} pad 被用来补位的字符，注意，仅支持一个字符
     */
    public static padLeft(str, len, pad) {
        if (str.length >= len) {
            return str
        }

        let diff = len - str.length
        let letters = MathUtil.createSameString(pad, diff)

        return letters + str;
    }
    /**
     * 对字符串向右补位
     * @param {*} str
     * @param {*} len
     * @param {*} pad
     */
    public static padRight(str, len, pad) {
        if (str.length >= len) {
            return str
        }

        let diff = len - str.length
        let letters = MathUtil.createSameString(pad, diff)

        return str + letters;
    }
    /**
     * 移除数字首尾的00
     * @param {*} input
     */
    public static clearNumberZero(input) {
        input = input.toString()
        var [integerPart, decimalPart = ''] = input.split('.')
        var isNegative = false
        if (integerPart.indexOf('-') === 0) {
            isNegative = true
            integerPart = integerPart.substring(1)
        }
        integerPart = integerPart.replace(/^0+/, '') // 去除开头的000
        decimalPart = decimalPart.replace(/0+$/, '') // 去除末尾的000
        var value = (isNegative && (integerPart || decimalPart) ? '-' : '') + (integerPart ? integerPart : '0') + (decimalPart ? '.' + decimalPart : '')
        return value
    }
    /**
     * 创建一个特定位数，且每个元素值相同的数组
     * @param {*} count
     * @param {*} value
     */
    public static createSameString(value, count = 1) {
        let str = '';
        while(count > 0){
            count -= 1;
            str += value;
        }
        return str;
        // return [].fill.call(new Array(count), value);
    }
    /**
     * 获取数字的字符串形式
     * @param {*} num
     */
    public static numerify(num) {
        if (typeof (num) == 'string') {
            if (!MathUtil.isNumeric(num)) {
                return ''
            }
            let value = MathUtil.clearNumberZero(num)
            return value
        }
        else if (MathUtil.isNumber(num)) {
            let value = num.toString()
            // num.toString()之后得到的指数有两种，一种是精确的小值指数形式，另一种是非精确的大值指数形式
            // 但无论哪一种，这里都是最终的办法，非精确的情况下，无法做到数值转化
            if (value.indexOf('e')) {
                return MathUtil.convertNumberWithExponential(value)
            }
            else {
                return value
            }
        }
        else {
            return ''
        }
    }
    /**
     * 判断是否为数字的字符串形式，支持小数点和负数
     * @param {*} value
     */
    public static isNumeric(value) {
        return typeof (value) == 'string' && /^\-{0,1}[0-9]+(\.{0,1}[0-9]+){0,1}$/.test(value)
    }
    public static isNumber(value) {
        return typeof value === 'number' && !isNaN(value)
    }
    /**
     * 将一个指数型数字转化为普通数字
     * @param {number|string} input 指数型数字，可以是一个同等合法的字符串
     * @return {string} 转化后的普通数字字符串，因为大值会被自动转化为指数型数字，因此，这里必须返回字符串
     * @example
     * convertNumberWithExponential(1.2e3) => '1200'
     */
    public static convertNumberWithExponential(input) {
        let num = parseFloat(input);
        if (isNaN(num)) {
            return '';
        }

        if (!input && input !== 0) {
            return '';
        }

        let str = input.toString();

        // 如果输入中根本不存在e
        // 当input为12.3e13的时候，str会直接由于.toString成为正常字符串
        if (str.indexOf('e') === -1) {
            return str;
        }

        // 假设input为一个合法的指数型数字或数字字符串，不考虑非法输入的情况
        let [base, exp] = str.split('e');
        let count = Number.parseInt(exp, 10);
        if (count >= 0) {
            let arr = base.split('');
            for (let i = 0; i < count; i++) {
                let index = arr.indexOf('.');
                let next = index === arr.length - 1 ? '0' : arr[index + 1];
                arr[index] = next;
                arr[index + 1] = '.';
            }
            if (arr[arr.length - 1] === '.') {
                arr.pop();
            }
            let result = arr.join('');
            return result;
        }
        else {
            // 通过反转数字，把小数点往后移，最后反转过来的方法实现负数指数转化
            let arr = base.split('');
            let rarr = arr.reverse();
            for (let i = count; i < 0; i++) {
                let index = rarr.indexOf('.');
                let next = index === rarr.length - 1 ? '0' : rarr[index + 1];
                rarr[index] = next;
                rarr[index + 1] = '.';
            }
            let rrarr = rarr.reverse();
            if (rrarr[0] === '.') {
                rrarr.unshift('0');
            }
            let result = rrarr.join('');
            return result;
        }
    }
    public static inArray(value, array) {
        let index = array.indexOf(value);
        if (index >= 0) {
            return true;
        }
        else {
            return false;
        }
    }
    //随机打乱数组
    public static shuffle(b) {
        var a = [].concat(b);
        var len = a.length;
        for (var i = 0; i < len; i++) {
            var end = len - 1;
            var index = (Math.random() * (end + 1)) >> 0;
            var t = a[end];
            a[end] = a[index];
            a[index] = t;
        }
        return a;
    }
}
